package com.sisgh.jefdv1.file;

import com.sisgh.jefdv1.exception.file.JavaFileException;
import com.sisgh.jefdv1.core.java.format.JavaFormat;
import com.sisgh.jefdv1.util.CloseUtils;
import com.sisgh.jefdv1.util.CollectionUtils;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.List;
import java.util.Set;

/**
 * Java文件写者
 */
public class JavaFileWriter {
    /**
     * 默认缓冲区大小
     */
    private static final int DEFAULT_BUFFER_SIZE = 4096;

    /**
     * 默认生成在当前目录下的 entities 目录中
     */
    private static final String DEFAULT_DIRECTORY = System.getProperty("user.dir") + "/entities/";
    private static final String SUFFIX = ".java";

    /**
     * fc 的参数
     */
    private static final Set<StandardOpenOption> options = Set.of(
            StandardOpenOption.TRUNCATE_EXISTING,
            StandardOpenOption.READ,
            StandardOpenOption.WRITE
    );


    private String dir;
    private boolean foldersByPackage;

    private List<JavaFormat> formats;

    private JavaFileWriter(List<JavaFormat> formats) {
        this.formats = CollectionUtils.normalize(formats);
        this.dir = DEFAULT_DIRECTORY;
        this.foldersByPackage = false;
    }

    /**
     * 创建默认写者
     * @param formats
     * @return
     */
    public static JavaFileWriter defaultWriter(List<JavaFormat> formats) {
        return new JavaFileWriter(formats);
    }

    /**
     * 设置路径
     * @param path
     * @return
     */
    public JavaFileWriter path(Path path) {
        this.dir = path.toAbsolutePath().toString();
        return this;
    }

    /**
     * 添加路径
     * @param path
     * @return
     */
    public JavaFileWriter appendPath(Path path) {
        this.dir += path.toAbsolutePath().toString();
        return this;
    }

    /**
     * 是否根据包名生成目录
     * @param foldersByPackage
     * @return
     */
    public JavaFileWriter foldersByPackage(boolean foldersByPackage) {
        this.foldersByPackage = foldersByPackage;
        return this;
    }

    /**
     * 设置需要格式化的数据
     * @param formats
     * @return
     */
    public JavaFileWriter formats(List<JavaFormat> formats) {
        this.formats = CollectionUtils.normalize(formats);
        return this;
    }

    /**
     * 创建文件及其目录
     * @param format
     * @return
     * @throws IOException
     */
    private Path createFile(JavaFormat format) throws IOException {
        Path file;
        String name = format.name() + SUFFIX;
        if (foldersByPackage) {
            String packPath = format.packPath();
            file = Paths.get(this.dir, packPath, name);
        } else {
            file = Paths.get(this.dir , name);
        }

        // 创建目录
        Files.createDirectories(file.getParent());
        // 创建文件
        Files.createFile(file);

        return file;
    }

    /**
     * 转换成 .java
     * @param format
     * @throws JavaFileException
     */
    private void doConvertJavaFile(JavaFormat format) throws JavaFileException {
        Path file;
        try {
            file = createFile(format);
        } catch (IOException e) {
            throw new JavaFileException("file create fail", e);
        }

        FileChannel fc = null;
        try {
            fc = FileChannel.open(file, options);
            ByteBuffer buffer = ByteBuffer.wrap(format.toString().getBytes());
            int write = fc.write(buffer);
            if (write == -1) {
                throw new JavaFileException("write fail");
            }
        } catch (IOException e) {
            throw new JavaFileException("write fail", e);
        } finally {
            CloseUtils.close(fc);
        }
    }

    /**
     * 转换成 .java（对外暴露）
     * @throws JavaFileException
     */
    public void convertJavaFile() throws JavaFileException {
        if (CollectionUtils.isEmpty(formats)) {
            throw new JavaFileException("No java format found");
        }

        for (JavaFormat format : formats) {
            doConvertJavaFile(format);
        }
    }
}
